Skip to content

docs(plans): environment profiles + host-to-host session teleport - #365

Merged
physercoe merged 3 commits into
physercoe:mainfrom
agentfleets:docs/env-teleport-plan
Jul 23, 2026
Merged

docs(plans): environment profiles + host-to-host session teleport#365
physercoe merged 3 commits into
physercoe:mainfrom
agentfleets:docs/env-teleport-plan

Conversation

@agentfleets

Copy link
Copy Markdown
Collaborator

What

A third design plan: docs/plans/env-profiles-and-session-teleport.md — two control-plane primitives from the cloud-agent comparison (Claude Code on the web / Codex cloud), grounded in the current codebase first. Docs-only.

Part 1 — Environment profiles (first-class env_profiles)

The gap: Claude/Codex web attach setup script + env vars + secrets + network policy to tasks as a reusable entity. Grounding showed termipod has no env/setup fields anywhere (templates, families, spawn spec — schema grep is zero), nothing but the hub-minted MCP token is injected into spawns, the egress proxy does URL-masking only, and the docs flag the gap themselves (multi-agent-harness-landscape.md:627).

The design, built around the zero-knowledge vault (ADR-052) instead of against it:

  • Team-scoped env_profiles entity: setup_script (hub-visible) + plain env_vars (hub-visible) + secret_refs (references into the vault — never values) + declarative network_policy.
  • Secret delivery honors forbidden-pattern deps(hub): bump modernc.org/sqlite from 1.34.4 to 1.50.1 in /hub #15: host device keys (X25519 keypair at registration, public key rides capabilities_json — same shape as ADR-052's per-device wrapping); the client resolves vault refs and seals a per-spawn envelope to the target host's key; the hub carries ciphertext it cannot decrypt.
  • Snapshot semantics at spawn (profile id + rev), deterministic env merge order, setup script after workdir creation with fail-closed default.
  • Wedges: E1 entity+plain env+setup script → E2 attach points (project config, spawn sheet) + UI (the vault's unused env/script item types finally get a consumer) → E3 secret envelopes (own ADR) → E4 network-policy enforcement via egress-proxy rules.

Part 2 — Session teleport between hub-registered hosts

Grounding: the resume primitive (resumePausedSession) already does terminate + respawn + keep session row + splice engine cursor — but hard-pinned to the same host (handlers_sessions.go:691), and engine session stores (~/.claude/projects, ~/.kimi-code/sessions, …) are host-local disk files (ADR-014's single-host assumption), so naive cross-host resume would cold-start. Transcripts are hub-side, so conversation history is portable for free.

The design: POST /sessions/{id}/teleport {target_host_id}

  1. Validate target (online via checkSpawnHostReachable, capability supports the family, repo reachable for worktree sessions).
  2. Workdir handoff: branch push/pull for worktree sessions (hub/<handle>); tar-via-hub-blob relay for non-worktree (T2, size-capped).
  3. Engine-state bundles: families declare state_paths globs in agent_families.yaml; source tars the engine store → hub blob → target restores with workdir-path remapping, so sessions.engine_session_id resolves on the target exactly as on the source. Degraded fallback: cold-start + session digest context file, explicit in UI.
  4. Terminate-after-verify swap — source is only terminated after the target spawn reports ready; failure leaves the source untouched.
  5. UX: teleport action with capability-filtered host picker and progress phases, on desktop first (T1), mobile in T2.

Wedges: T1 worktree sessions + kimi/claude state bundles → T2 non-worktree + remaining families + mobile → T3 failover/drain/bare-repo relay.

Security / ADR notes called out

New ADR for E3 (extends ADR-052 to hosts); engine-state blobs flagged as transcript-equivalent sensitivity (seal-to-host via E3 keys recommended in T3); workdir tar caps + blob TTL; setup scripts fail-closed.

Open questions (§end)

Profile scoping vs per-project key overrides; mobile as secret-ref resolver; engine-state blob sensitivity gate for T1; teleport of active-but-idle vs paused-only; non-git tar size cap.

Verification

  • scripts/lint-docs.sh ✓ (cross-link to the now-merged transcript plan resolves)

agentfleet and others added 3 commits July 23, 2026 00:50
Two control-plane primitives grounded in the current codebase:
- env_profiles: first-class setup-script + env-vars + network-policy
  entity; secret values stay in the zero-knowledge vault as references,
  delivered per-spawn as envelopes sealed to host device keys
  (hub-blind, extends ADR-052's pattern; new ADR flagged for E3).
- session teleport: re-targets the existing resume machinery
  (terminate-after-verify) across hub hosts; engine-state bundles via
  per-family state_paths (tar -> hub blob -> target) so cross-host
  resume doesn't cold-start; branch handoff for worktree sessions,
  workdir tar relay for the rest.
…d transport, envelope re-seal, host-key trust, decisions

Maintainer review amendments before merge:
- Teleport reordered pause-first per ADR-014's single-appender
  invariant: terminate-after-verify cut the engine-state bundle while
  the source engine could still append (silent turn loss) and put two
  engines on one logical store during the verify window. Pause is now
  step 2; every later failure falls back to resume-on-source — the
  existing resumePausedSession path.
- Transport section added: /v1/blobs caps at 25 MiB and buffers
  uploads in memory, so single-blob tars (256 MB workdir cap, large
  engine stores) are impossible — teleport rides a chunked bundle
  manifest (<=25 MiB parts + manifest blob + whole-tar hash).
- E3 x Part 2 interaction stated: env_secret_envelope is sealed to the
  source host's key; the initiating client re-resolves and re-seals to
  the target host during teleport; headless teleport of secret-bearing
  sessions is impossible by design.
- E3 host-key enrollment gains ADR-052 D-4's explicit trust step
  (fingerprint short-code confirm, pinned thereafter, re-key =
  re-trust) — without it a hub could substitute keys and open every
  envelope.
- env_profile_rev dropped: the entity has no revision machinery;
  snapshot = materialize resolved env_vars + setup_script into the
  spawn spec, env_profile_id kept for provenance.
- Open questions closed as decisions; companion links updated (physercoe#363
  c97c522, physercoe#364 c797538; branch merged with main so both resolve).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
physercoe
physercoe previously approved these changes Jul 23, 2026
@physercoe
physercoe merged commit fb8e5f1 into physercoe:main Jul 23, 2026
3 checks passed
physercoe pushed a commit that referenced this pull request Jul 27, 2026
…pes (E3)

Discharges the env-profiles plan's 'E3 needs its own ADR' gate, extending
ADR-052 D-4/D-5 to hosts under amended forbidden-pattern #15:

D-1 per-host X25519 identity keypair (StateDir 0600, never leaves host;
pubkey + env_envelope_v capability ride capabilities_json). D-2 explicit
operator trust — fingerprint short-code (host console vs client), pin
stored INSIDE the zero-knowledge vault bundle (silent TOFU and hub-side
pins rejected: hub-relayed keys make substitution trivial — the #365
review amendment made normative). D-3 envelope v1 on the vault's
existing sealed-box (x25519-dalek/aes-gcm + Dart cryptography — no new
crypto), AAD binds team|host_id|profile so the hub cannot re-target
ciphertext; same-host replay accepted (enables client-free restarts).
D-4 hub rejects secret-bearing spawns without an envelope (flips E1b's
loud warning; headless secret spawns fail by design). D-5 injection via
real process env only — driver Env slice / tmux -e — NEVER the E1b
envExportPrefix launch string (ps/tmux-scrollback/spec leakage);
present/absent-only logging. D-6 teleport re-seal by the initiating
client (headless teleport of secret sessions impossible by design).
D-7 snapshot semantics, re-key invalidates pins+envelopes, no remote
revocation pretense.

Consequences pin the cross-implementation CI discipline: envelopes
produced by vault-core AND vault_crypto.dart must open in the Go
host-runner (three implementations, one construction). ADR index row +
plan E3 wedge now points at the ADR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants